# 描述: MQTT示例
# 作者: 王茂林
# 时间: 2023/5/10 10:15

import paho.mqtt.client as mqtt
import json
import uuid
import time
import common.logger as logger


class MQTT:

    def __init__(self, client_id, username, pwd, host, port=1883):
        self.client_id = client_id
        self.username = username
        self.pwd = pwd
        self.host = host
        self.port = port
        logger.info(f'-----------------MQTT info begin-----------------')
        logger.info(f'client_id = {self.client_id}')
        logger.info(f'username = {self.username}')
        logger.info(f'pwd = {self.pwd}')
        logger.info(f'host = {self.host}')
        logger.info(f'port = {self.port}')
        logger.info(f'-----------------MQTT info end-----------------')

        # 需要订阅的topic列表
        self.subscribe_topics = []

        # 默认MQTT需要断线重连
        self.need_reconnect_mqtt = True
        # 实例化MQTT client
        self.client = mqtt.Client(self.client_id)
        # 连接到MQTT服务器
        self.client.username_pw_set(self.username, self.pwd)
        # 设置 MQTT 连接回调函数
        self.client.on_connect = self.connect_callback
        self.client.on_disconnect = self.disconnect_callback
        self.client.on_subscribe = self.subscribe_callback

    def set_subscribe(self, topics, on_message_callback):
        """
        订阅设置
        :param topics:
        :param on_message_callback:
        :return:
        """
        self.client.on_message = on_message_callback
        self.subscribe_topics = topics if topics is not None else []

    def __del__(self):
        """
        类销毁，回收释放资源
        :return:
        """
        self.disconnect()

    def connect(self):
        """
        连接MQTT
        :return:
        """
        try:
            # if self.client.is_connected():
            #     logger.info(f"MQTT已连接，无需重复连接。")
            #     return
            logger.info(f"尝试连接MQTT[{self.host}:{self.port}]")
            rc = self.client.connect(self.host, int(self.port))
            logger.info(f"连接MQTT成功,返回码[{rc}]")
            # 保持连接
            self.client.loop_start()
        except Exception as e:
            logger.error(f"连接MQTT异常: {repr(e)}, 10秒后将再次尝试")
            # 10秒尝试重连一次
            time.sleep(10)
            self.connect()

    def disconnect(self):
        """
        断开连接
        :return:
        """
        try:
            # 正常关闭连接，不需要重连
            self.need_reconnect_mqtt = False
            if self.client is not None:
                logger.info(f"尝试断开MQTT[{self.host}:{self.port}]")
                self.client.loop_stop()
                self.client.disconnect()
        except Exception as e:
            logger.error(f"断开连接失败: {repr(e)}")

    def disconnect_callback(self, client, userdata, rc):
        """
        MQTT 断开连接回调函数
        :return:
        """
        logger.info(f"MQTT连接已断开，返回码: {rc}")
        # 需要重连，则尝试重连
        if self.need_reconnect_mqtt:
            while True:
                try:
                    logger.info(f"尝试重连MQTT...")
                    rc = client.reconnect()
                    logger.info(f"MQTT重连成功,返回码[{rc}]")
                    # 保持连接
                    client.loop_start()
                    break
                except Exception as e:
                    logger.error(f"MQTT重连失败: {repr(e)}, 10秒后将再次尝试")
                    # 重连失败，等待10秒后再次尝试
                    time.sleep(10)

    def connect_callback(self, client, userdata, flags, rc):
        """
        MQTT 连接回调函数
        :param client:
        :param userdata:
        :param flags:
        :param rc:
        :return:
        """
        if rc == 0:
            logger.info(f"MQTT连接成功,连接状态[{client.is_connected()}]")
            # 订阅topic
            if len(self.subscribe_topics) > 0:
                # 订阅topic
                self.client.subscribe([(t, 0) for t in self.subscribe_topics])
        else:
            logger.error(f"MQTT失败，返回码：{rc}")

    def subscribe_callback(self, client, userdata, mid, granted_qos):
        """
        MQTT 订阅topic成功回调函数
        :param client:
        :param userdata:
        :param mid:
        :param granted_qos:
        :return:
        """
        logger.info(f"成功订阅Topics: {self.subscribe_topics}")

    def publish(self, topic, payload):
        """
        发布消息
        :param topic:
        :param payload:
        :return:
        """
        try:
            self.client.publish(topic, payload)
        except Exception as e:
            logger.error(f"MQTT发布消息异常: {repr(e)}")

    def reset_subscribe(self, topics, on_message_callback):
        """
        重置订阅（会清除掉之前的订阅）
        :param topics: 要订阅的topic列表
        :param on_message_callback: 消息回调函数
        :return:
        """
        self.client.on_message = on_message_callback
        # 取消之前的订阅
        if len(self.subscribe_topics) > 0:
            self.client.unsubscribe([(t, 0) for t in self.subscribe_topics])
            logger.info(f'取消订阅Topic: {self.subscribe_topics}')
        self.subscribe_topics = topics if topics is not None else []
        # 订阅topic
        if len(self.subscribe_topics) > 0:
            # 订阅topic
            self.client.subscribe([(t, 0) for t in self.subscribe_topics])
            logger.info(f'订阅Topic: {self.subscribe_topics}')


if __name__ == '__main__':
    client_id = 'KrZWhraW6sz_meetingroom-1_0_0_1683685591440'
    username = 'KrZWhraW6sz_meetingroom-1'
    pwd = 'CC87B53FE96C929825472CE6FC4D93F3C12A83459E2A941C98403987B4C3839B'
    host = 'prod.rlinkiot.com'
    port = 4435
    mqtt_client = MQTT(client_id, username, pwd, host, port)

    topic = 'sys/KrZWhraW6sz/meetingroom-1/thing/device/property/report'
    data = {
        "requestId": str(uuid.uuid1()),
        "version": "1.0",
        "params": {
            "modules": [
                {
                    "moduleName": "default",
                    "properties": {
                        "sszxrs": 8
                    }
                }
            ]
        }
    }
    payload = json.dumps(data)
    logger.debug(f'发送数据：{payload}')
    mqtt_client.publish(topic, payload)
    mqtt_client.disconnect()
